using System;
using System.Collections.Generic;
using System.Text;
using Sedodream.Sudoku.Exceptions;
using Sedodream.Sudoku.Events;
using System.Diagnostics;

namespace Sedodream.Sudoku.Board
{
	[DebuggerDisplay("{ToString()}")]
	public class BoardData : IBoardData,ICloneable
	{
		#region Fields
		private int sideLength;
		private int size;
		private int?[,] data;
		private const int defaultBoardSize = 3;
		private bool handleEvents;
		#endregion

		#region Events
		public event BoardDataChanged BoardChanged;
		#endregion

		#region Constructors
		private BoardData()
		{
			handleEvents = false;
			this.size = defaultBoardSize;
			this.sideLength = size * size;
			this.data = new int?[sideLength, sideLength];
			//this.BoardChanged +=new BoardDataChanged(BoardData_BoardChanged);
			handleEvents = true;
		}
		public BoardData(int size)
			: this()
		{
			handleEvents = false;
			if (size < 3)
			{
				string message = string.Format("Invalid board size value: {0}", size);
				throw new BoardSizeException(message);
			}
			this.size = size;
			this.sideLength = size * size;
			this.data = new int?[sideLength, sideLength];
			handleEvents = true;
		}
		public BoardData(int size, int?[,] cells)
			: this(size)
		{
			handleEvents = false;
			if (cells == null || cells.Length <= 0 )
			{
				string message = string.Format("Unable to create board, becaause data provided is empty");
				throw new BoardCreationException(message);
			}
			int sideLength = cells.GetUpperBound(0)+1;
			if (sideLength <= 0)
			{
				string message = string.Format("Unable to create board, because of a dimension mis-match");
				throw new BoardCreationException(message);
			}
			int testSize = (int)Math.Sqrt((double)sideLength);
			if (testSize != size)
			{
				string message = string.Format("Unable to create board, because of a dimension mis-match");
				throw new BoardCreationException(message);
			}

			this.data = new int?[sideLength, sideLength];
			for(int rowIndex =0;rowIndex<sideLength;rowIndex++)
			{
				for(int colIndex = 0;colIndex<sideLength;colIndex++)
				{
					this.data[rowIndex, colIndex] = cells[rowIndex, colIndex];
				}
			}

			//this.data = cells;
			handleEvents = true;
		}
		#endregion
		void BoardData_BoardChanged(object sender, BoardChangedEventArgs args)
		{
			//dont do anything yet
			//throw new Exception("The method or operation is not implemented.");
		}
		#region IBoard implementation
		public int SideLength
		{
			get { return this.sideLength; }
		}
		public int Size
		{
			get { return size; }
			set { size = value; }
		}
		public int?[,] Data
		{
			get
			{
				return data;
			}
		}
		public int? this[int row, int col]
		{
			get{return data[row, col];}
			set
			{
				if (data[row, col] == value)
					return;
				this.data[row, col] = value;
				if (this.BoardChanged != null && handleEvents)
					this.BoardChanged(this, new BoardChangedEventArgs(this, new Point(row, col)));
			}
		}
		public void SetValueAt(int row, int col, int? value)
		{
			if (data[row, col] == value)
				return;
			
			this.data[row, col] = value;
			if (BoardChanged != null)
				BoardChanged(this, new BoardChangedEventArgs(this, new Point(row, col)));
		}
		#endregion
		#region ICloneable Members

		public object Clone()
		{
			int?[,] copy = new int?[SideLength, SideLength];
			for (int rowIndex = 0; rowIndex < SideLength; rowIndex++)
			{
				for (int colIndex = 0; colIndex < SideLength; colIndex++)
				{
					copy[rowIndex, colIndex] = this[rowIndex, colIndex];
				}
			}
			BoardData clone = new BoardData(this.Size, copy);
			return clone;
		}

		#endregion	
		public override string ToString()
		{
			if (SideLength <= 0)
				return "[]";

			StringBuilder sb = new StringBuilder();
			for (int rowIndex = 0; rowIndex < SideLength; rowIndex++)
			{
				if (rowIndex != 0 && rowIndex % Size == 0)
					sb.Append(Environment.NewLine);
				for (int colIndex = 0; colIndex < SideLength; colIndex++)
				{
					if (colIndex != 0 && colIndex % Size==0)
					{
						sb.Append(" ");
					}
					int? cellValue = this[rowIndex,colIndex];
					string cellStr = cellValue.HasValue ? cellValue.Value.ToString() : "0";
					sb.Append(cellStr);
				}
				sb.Append(Environment.NewLine);
				
			}

			return sb.ToString();
		}
	}
}
